﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a shopping cart item.
    /// </summary>
    public class CreateShoppingCartItem : CreateAuditable<ShoppingCartItemEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the cart identifier.
        /// </summary>
        public int CartID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sales item identifier.
        /// </summary>
        public int SalesItemID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the quantity.
        /// </summary>
        public decimal Quantity
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price.
        /// </summary>
        public decimal Price
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public ShoppingCartItemFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Quantity <= Decimal.Zero)
                return Err.Of<ShoppingCartItemEntity>().NotValid("Quantity");
            if(Price <= Decimal.Zero)
                return Err.Of<ShoppingCartItemEntity>().NotValid("Price");

            var salesItem = uow.Execute(new GetSalesItem
            {
                ThrowsException = true,
                EntityID = SalesItemID
            }).Result;
            if(Price != salesItem.Price && !salesItem.Flags.HasFlag(SalesItemFlags.VariablePricing))
                return Err.Of<ShoppingCartItemEntity>().NotValid("Price");

            var dtNow = DateTime.UtcNow;
            if(salesItem.AvailableFrom > dtNow || salesItem.AvailableTo < dtNow)
                return Err.Of<SalesItemEntity>().NotAvailable();

            bool inStock = uow.Execute(new CheckStockReserve
            {
                SKU = salesItem.SKU,
                Quantity = Quantity
            }).Result;
            if(!inStock)
                return Err.Of<SalesItemEntity>().OutOf("Stock");

            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out ShoppingCartItemEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateShoppingCartItemTotal
            {
                EntityID = result.EntityID
            });
        }

        protected override void Construct(IUnitOfWork uow, ShoppingCartItemEntity entity)
        {
            base.Construct(uow, entity);

            entity.Cart = uow.Execute(new GetShoppingCart
            {
                ThrowsException = true,
                EntityID = CartID
            }).Result;
            entity.SalesItem = uow.Execute(new GetSalesItem
            {
                ThrowsException = true,
                EntityID = SalesItemID
            }).Result;
            entity.Quantity = Quantity;
            entity.Price = Price;
            entity.Total = Decimal.Zero;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the shopping cart item.
    /// </summary>
    public class UpdateShoppingCartItem : UpdateAuditable<ShoppingCartItemEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the quantity.
        /// </summary>
        public decimal Quantity
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price.
        /// </summary>
        public decimal Price
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public ShoppingCartItemFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Quantity <= Decimal.Zero)
                return Err.Of<ShoppingCartItemEntity>().NotValid("Quantity");
            if(Price <= Decimal.Zero)
                return Err.Of<ShoppingCartItemEntity>().NotValid("Price");

            var item = uow.Execute(new GetShoppingCartItem
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;
            var salesItem = item.SalesItem;
            if(Price != salesItem.Price && !salesItem.Flags.HasFlag(SalesItemFlags.VariablePricing))
                return Err.Of<ShoppingCartItemEntity>().NotValid("Price");
            
            bool inStock = uow.Execute(new CheckStockReserve
            {
                SKU = salesItem.SKU,
                Quantity = Quantity
            }).Result;
            if(!inStock)
                return Err.Of<SalesItemEntity>().OutOf("Stock");

            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out ShoppingCartItemEntity result)
        {
            var item = uow.Execute(new GetShoppingCartItem
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;
            decimal oldQuantity = item.Quantity;
            decimal oldPrice = item.Price;

            base.Work(uow, out result);

            if(oldQuantity == result.Quantity && oldPrice == result.Price) 
                return;

            uow.Execute(new UpdateShoppingCartItemTotal
            {
                EntityID = result.EntityID
            });
        }

        protected override void Revise(IUnitOfWork uow, ShoppingCartItemEntity entity)
        {
            base.Revise(uow, entity);

            entity.Quantity = Quantity;
            entity.Price = Price;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the shopping cart item total.
    /// </summary>
    public class UpdateShoppingCartItemTotal : OperateEntityByID<ShoppingCartItemEntity>
    {
        #region Utilities

        protected override void Work(IUnitOfWork uow, out ShoppingCartItemEntity result)
        {
            var item = uow.Execute(new GetShoppingCartItem
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;

            uow.Flush();
            uow.SqlQuery("exec fwk_ShoppingCartItems_UpdateTotal @ItemID=:ItemID, @UpdatedOn=:UpdatedOn")
                .SetInt32("ItemID", item.EntityID)
                .SetDateTime("UpdatedOn", DateTime.UtcNow)
                .ExecuteUpdate();
            uow.Flush();
            
            result = uow.Execute(new RefreshEntity<ShoppingCartItemEntity>
            {
                Entity = item
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the shopping cart item.
    /// </summary>
    public class DeleteShoppingCartItem : DeleteEntity<ShoppingCartItemEntity>, 
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow)
        {
            var cart = uow.Execute(new GetShoppingCartItem
            {
                EntityID = EntityID
            }).Result.Cart;

            base.Work(uow);

            uow.Execute(new UpdateShoppingCartTotal
            {
                EntityID = cart.EntityID
            });
        }

        protected override void Destruct(IUnitOfWork uow, ShoppingCartItemEntity entity)
        {
            foreach(var i in entity.Options.ToList())
            {
                uow.Execute(new UnmapSalesOptionFromShoppingCartItem
                {
                    EntityID = entity.EntityID,
                    ChildID = i.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the shopping cart item by identifier.
    /// </summary>
    public class GetShoppingCartItem : GetEntity<ShoppingCartItemEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Map__

    /// <summary>
    /// Maps the option to item.
    /// </summary>
    public class MapSalesOptionToShoppingCartItem : MapOperation
        <ShoppingCartItemEntity, SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var customer = ctx.Customer(uow);
                        if(customer == null || !customer.Flags.HasFlag(CustomerFlags.Active))
                            return false;
                        var item = uow.Execute(new GetShoppingCartItem
                        {
                            EntityID = EntityID
                        }).Result;
                        if(item == null)
                            return false;
                        return item.Cart.Customer.Equals(customer);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(uow.Execute(new GetSalesOption
            {
                ThrowsException = true,
                EntityID = ChildID
            }).Result.Flags.HasFlag(SalesOptionFlags.Order))
            {
                return Err.Of(this).NotAllowed();
            }
            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out ShoppingCartItemEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateShoppingCartItemTotal
            {
                EntityID = EntityID
            });
        }

        #endregion
    }

    /// <summary>
    /// Unmaps the option from item.
    /// </summary>
    public class UnmapSalesOptionFromShoppingCartItem : UnmapOperation
        <ShoppingCartItemEntity, SalesOptionEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var customer = ctx.Customer(uow);
                        if(customer == null || !customer.Flags.HasFlag(CustomerFlags.Active))
                            return false;
                        var item = uow.Execute(new GetShoppingCartItem
                        {
                            EntityID = EntityID
                        }).Result;
                        if(item == null)
                            return false;
                        return item.Cart.Customer.Equals(customer);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out ShoppingCartItemEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateShoppingCartItemTotal
            {
                EntityID = EntityID
            });
        }

        #endregion
    }

    #endregion
}
